package cn.com.idmy.base.model;

import com.alibaba.fastjson2.annotation.JSONField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Data
@Accessors(fluent = true)
@Schema(title = "分页数据结构")
public class Page<T> implements Serializable {
    @Valid
    @Schema(title = "查询参数")
    @NotNull(message = "查询参数不能为空")
    @JSONField(serialize = false)
    protected T params;

    @Schema(title = "分页数据列表")
    protected List<T> rows;

    @Schema(title = "当前页码", example = "1", minimum = "1")
    @JSONField(serialize = false)
    protected int pageNo;

    @Max(10000)
    @JSONField(serialize = false)
    @Schema(title = "每页显示记录数", example = "10", minimum = "1", maximum = "10000")
    protected int pageSize;

    @Schema(title = "总记录数")
    protected long total;

    @Schema(title = "是否需要统计总数")
    protected Boolean hasTotal;

    @JSONField(serialize = false)
    @Schema(title = "排序字段数组", example = "[ 'name', 'desc', 'createdAt', 'asc' ]")
    protected String[] sorts;

    @JSONField(serialize = false)
    @Schema(title = "字段数组", example = "[ 'name', 'createdAt' ]")
    protected String[] fields;

    public int pageNo() {
        return pageNo < 1 ? 1 : pageNo;
    }

    public int pageSize() {
        return pageSize < 1 ? 10 : pageSize;
    }

    @JSONField(serialize = false, deserialize = false)
    public int size() {
        return rows == null ? 0 : rows.size();
    }

    @JSONField(serialize = false, deserialize = false)
    public int offset() {
        return pageSize() * (pageNo() - 1);
    }

    /**
     * Page 的泛型转换
     *
     * @param mapper 转换函数
     * @param <R>    转换后的泛型
     * @return 转换泛型后的 Page
     */
    @JSONField(serialize = false, deserialize = false)
    public <R> Page<R> convert(Function<? super T, ? extends R> mapper) {
        Page<R> out = (Page<R>) this;
        out.rows = rows.stream().map(mapper).collect(Collectors.toList());
        return out;
    }

    @JSONField(serialize = false, deserialize = false)
    public void forEach(Consumer<T> consumer) {
        if (rows != null) {
            rows.forEach(consumer);
        }
    }

    @JSONField(serialize = false, deserialize = false)
    public Stream<T> stream() {
        return rows == null ? Stream.empty() : rows.stream();
    }

    @JSONField(serialize = false, deserialize = false)
    public <R> Collection<R> keys(Function<T, R> mapper, Collector<? super R, ?, ? extends Collection<R>> collector) {
        return rows == null ? Collections.emptyList() : stream().map(mapper).collect(collector);
    }

    @JSONField(serialize = false, deserialize = false)
    public <R> Collection<R> keys(Function<T, R> mapper) {
        return keys(mapper, Collectors.toList());
    }

    @JSONField(serialize = false, deserialize = false)
    public boolean isEmpty() {
        return rows == null || rows.isEmpty();
    }

    @JSONField(serialize = false, deserialize = false)
    public boolean isNotEmpty() {
        return !isEmpty();
    }

    public static <T> Page<T> empty() {
        return of(Collections.emptyList());
    }

    public static <T> Page<T> of(int pageNo, int pageSize) {
        return of(Collections.emptyList(), pageNo, pageSize, 0L);
    }

    public static <T> Page<T> of(List<T> rows) {
        return of(rows, 0, 0, 0L);
    }

    public static <T> Page<T> of(@NotNull List<T> rows, long total) {
        return of(rows, 0, 0, total);
    }

    public static <T> Page<T> of(Supplier<List<T>> fn, long total) {
        return total > 0 ? of(fn.get(), 0, 0, total) : Page.empty();
    }

    public static <T> Page<T> of(List<T> rows, int pageNo, int pageSize, long total) {
        if (total == 0 && !rows.isEmpty()) {
            total = rows.size();
        }
        return new Page<T>().rows(new ArrayList<>()).pageNo(pageNo).pageSize(pageSize).rows(rows).total(total);
    }
}